<template>
  <div class="app-container">
    <uploader
      ref="myUploader"
      :fileStatusText="fileStatusText"
      :options="options"
      :autoStart="false"
      @file-added="onFileAdded"
      @file-success="onFileSuccess"
      @file-error="onFileError"
      class="uploader-app"
    >
      <uploader-unsupport></uploader-unsupport>

      <!-- 这里我把选择上传文件按钮和拖拽组件结合在一起使用了-->
      <uploader-btn ref="uploadBtn">
        <uploader-drop @click.native="()=>{$refs.uploadBtn.click}">
          <p>请点击虚线框选择要上传的文件或拖拽文件到虚线框内</p>
        </uploader-drop>
      </uploader-btn>

      <uploader-list>
        <div class="file-panel" slot-scope="props">
          <ul class="file-list">
            <li v-for="file in props.fileList" :key="file.id">
              <uploader-file :class="'file_' + file.id" ref="files" :file="file" :list="true"></uploader-file>
            </li>
            <div class="no-file" v-if="!props.fileList.length">暂无待上传文件</div>
          </ul>
        </div>
      </uploader-list>
    </uploader>

  </div>
</template>

<script>
export default {
  //https://svn1.psbc.com:7002/usvn/public/group/Group_CCS_Developer
  //http://devops.psbc.com/jira/secure/Dashboard.jspa
    data() {
        const _this = this
        return {
            // 用于替换组件原来的状态显示文字
            filsStatusText: {
                success:"成功",
                error:"失败",
                uploading:"上传中",
                paused:"暂停",
                waiting:"等待中"
            },
            // uploader 的主要配置
            options: {
                chunkSize: 2.5 * 1024 * 1024, // 允许片段最大为5M，因为最后一块默认是可以大于等于这个值，但必须小于它的两倍。
                simultaneousUploads: 5, // 最大并发上传数
                target:"xxx", // 目标上传 URL，若测试和上传接口不是同个路径，可以用函数模式
                permanentErrors:[404,415,500,501,409],// 原来默认是没有409的，但我这接口有409报错，需进入错误回调函数中提示错误信息
                // 每次发起测试校验，所有分片都会进入这个回调
                checkChunkUploadedByResponse:function (chunk,message) {
                    // 每次校验，chunk（片段）会不一样，但message（只有一个测试请求）一样
                    let objMessage = JSON.parse(message);
                    
                    // 后端认为这个文件上传过，则直接跳过
                    if(objMessage.skipUpload) return true; 
                    
                    // 若文件检测出现异常，则提示并返回false
                    if(objMessage.error) {
                        _this.$message.error(objMessage.message);
                        return false
                    }
                    
                    // 一些校验信息，需要用到的，可以绑定在chunk上
                    chunk.xxx = objMessage.xxx; // 可写可不写，看自己情况
                    
                    // chunk的offset就是分块后，该块片段在文件所有模块的位置
                    return (objMessage.uploadedList||[]).indexOf(chunk.offset+1)>=0
                },
                // 处理所有请求的参数
                processParams:(params,file,chunk) => {
                    // 这里需要根据后端的要求，处理一些请求参数
                    params.xxx = chunk.xxx // 比如一些需要在上传时，带上测试校验返回的一些信息字段
                    return params;
                }
            }
        }
    },
    methods:{
        // 导入文件时
        onFileAdded(file) {
            // 计算文件 MD5 并标记文件
            this.computeMD5(file);
        },
        // 上传失败
        onFileError(rootFile,file,res) {
            this.$message.error(JSON.parse(res).message);
        },
        // 取消文件上传
        onFileRemoved(file) {
            // 发起取消上传请求给后端
            this.$axios.cancelUploadFile({
                filename:file.name,
                identifier:file.uniqueIdentifer // 文件标记
            })
        },
        // 所有片段上传成功后，进入文件上传成功回调
        onFileSuccess(rootFile,file,res) {
            res = JSON.parse(res);
            // 后端返回成功的字段，插件认为只要所有片段的上传请求都成功了就是上传成功了，而对于其他的错误它是无法处理的
            if (!res.result) {
                this.$message.error(res.message);
                return
            }
            // 如果后端返回可以合并的字段则发起合并请求
            if(res.needMerge) {
                // 获取组件的成功状态显示dom节点
                const metaDom = document.querySelector(`.uploader-file.file_${file.id} .uploader-file-status`);
                // 因为插件在所有片段请求成功后就显示上传成功的状态
                // 可合并是否成功却不管了，而插件并未提供处理方式
                // 只能通过节点操作修改状态来处理了 
                metaDom.innerText = "合并中..."; 
                //this.$axios.mergeChunk({...}); // 发起合并请求
            } else {
                // 分片上传成功，但整个文件上传并未结束，不需要合并
                console.log("上传成功")
            }
        },
        // 根据文件内容计算 MD5 并标记文件 file
        computeMD5(file) {
            let fileReader = new FileReader();
            let time = new Date().getTime();
            let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
            let currentChunk = 0;
            const chunkSize = this.options.chunkSize;
            let chunks = Math.ceil(file.size / chunkSize); // 总模块数
            let spark = new SparkMD5.ArrayBuffer();
    
            // 文件状态设为"计算MD5"
            this.statusSet(file.id, 'md5');
    
            file.pause();// 先暂停文件的上传过程
    
            loadNext(); // 开始读取文件内容
            
            // FileReader 加载完成
            fileReader.onload = (e => {
                // 插入读取的片段内容到 SparkMD5 中
                spark.append(e.target.result);
                
                // 按分片顺序读取，小于最后模块位置的就继续读取
                if (currentChunk < chunks) {
                    currentChunk++;
                    // 实时展示MD5的计算进度
                    // 对于大型文件读取内容还是会花不少时间
                    // 所以需要显示读取进度在界面
                    let dom = document.querySelector(`.uploader-file.file_${file.id} .uploader-file-meta`);
                    let md5Progress = ((currentChunk/chunks)*100).toFixed(0);
                    if (md5Progress < 100) {
                        dom.innerText = "MD5校验："+md5Progress+"%"; 
                    }  else if(md5Progress === 100) {
                        dom.innerText = "MD5校验完成"
                    }
                    loadNext();
                } else {
                    // 所有文件分片内容读取完成，生成MD5
                    let md5 = spark.end();
                    // 在computeMD5Success中标记文件
                    this.computeMD5Success(md5, file);
                    console.log(`MD5计算完毕：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${file.size} 用时：${new Date().getTime() - time} ms`);
                }
            });
            // FileReader 加载失败
            fileReader.onerror = function () {
                this.error(`文件${file.name}读取出错，请检查该文件`)
                file.cancel();
            };
            
            // 分片读取文件内容函数
            function loadNext() {
                let start = currentChunk * chunkSize;
                let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
                // fileReader 读取文件
                fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
            }
        },
        computeMD5Success(md5,file) {
            file.uniqueIdentifer = md5;// 标记文件file
            file.resume();// 继续上传
        }
    }
}

</script>